{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2.2 扩展答疑机器人的知识范围\n",
    "\n",
    "## 🚄 前言\n",
    "\n",
    "你已经了解到RAG是扩展大模型知识范围的有效解决方案。本节你将学习RAG的工作流程，以及如何创建一个RAG应用，使它能够根据公司的制度文件来进行回答。\n",
    "\n",
    "## 🍁 课程目标\n",
    "\n",
    "学完本课程后，你将能够：<br>\n",
    "1. 了解RAG的工作流程\n",
    "2. 创建一个RAG应用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. RAG的工作原理\n",
    "\n",
    "你在考试的时候有可能会因为忘记某个概念或公式而失去分数，但考试如果是开卷形式，那么你只需要找到与考题最相关的知识点，并加上你的理解就可以进行回答了。\n",
    "\n",
    "对于大模型来说也是如此，在训练过程中由于没有见过某个知识点（比如你们公司的制度文件），因此直接向它提问相关问题会得到不准确的答案；如果在大模型生成内容时，像开卷考试一样将相关知识提供给它作为参考，那么大模型回答的质量也就会大幅提高了。\n",
    "\n",
    "RAG（Retrieval Augmented Generation）就是为大模型提供参考资料的解决方案。RAG应用通常包含**建立索引**与**检索生成**两部分。\n",
    "\n",
    "### 1.1 建立索引\n",
    "你可能会在考试前对参考资料做标记，来帮助你在考试时更容易地找到相关信息。类似的，RAG应用往往也会在回答前就已经做好了标记，这一过程叫做**建立索引**，建立索引包括四个步骤：<br>\n",
    "1. **文档解析**<br>\n",
    "就像你会将书上看到的视觉信息理解为文字信息一样，RAG应用也需要首先将知识库文档进行加载并解析为大模型能够理解的文字形式。\n",
    "2. **文本分段**<br>\n",
    "你通常不会在做某道题时把整本书都翻阅一遍，而是去查找与问题最相关的几个段落，因此你会先把参考资料做一个大致的分段。类似的，RAG应用也会在文档解析后对文本进行分段，以便于在后续能够快速找到与提问最相关的内容。\n",
    "3. **文本向量化**<br>\n",
    "在开卷考试时，你通常会先在参考资料中寻找与问题最相关的段落，再去进行作答。在RAG应用中，通常需要借助嵌入（embedding）模型分别对段落与问题进行数字化表示，在进行相似度比较后找出最相关的段落，数字化表示的过程就叫做文本向量化。<br>\n",
    "    > 如果你对这一过程的细节感兴趣，可以学习-本教程的拓展阅读部分。\n",
    "4. **存储索引**<br>\n",
    "存储索引将向量化后的段落存储为向量数据库，这样RAG应用就无需在每次进行回复时都重复以上步骤，从而可以增加响应速度。\n",
    "\n",
    "    <img src=\"https://gw.alicdn.com/imgextra/i2/O1CN010zLf411zVoZQ9cWsI_!!6000000006720-2-tps-1592-503.png\" width=\"600\"><br>\n",
    "\n",
    "    在建立索引后，RAG应用就可以根据用户的问题检索出相关的文本段了。\n",
    "\n",
    "### 1.2 检索生成\n",
    "检索、生成分别对应着RAG名字中的`Retrieval`与`Generation`两阶段。**检索**就像开卷考试时去查找资料的过程，**生成**则是在找到资料后，根据参考资料与问题进行作答的过程。<br>\n",
    "1. **检索**<br>\n",
    "检索阶段会召回与问题最相关的文本段。通过embedding模型对问题进行文本向量化，并与向量数据库的段落进行语义相似度的比较，找出最相关的段落。检索是RAG应用中最重要的环节，你可以想象如果考试的时候找到了错误的资料，那么回答一定是不准确的。为了提高检索准确性，除了使用性能强大的embedding模型，也可以做重排（rerank）、句子窗口检索等方法，这些内容你可以在下一章节进行学习。\n",
    "2. **生成**<br>\n",
    "在检索到相关的文本段后，RAG应用会将问题与文本段通过提示词模板生成最终的提示词，由大模型生成回复，这个阶段更多是利用大模型的总结能力，而不是大模型本身具有的知识。\n",
    "    > 一个典型的提示词模板为：`请根据以下信息回答用户的问题：{召回文本段}。用户的问题是：{question}。`\n",
    "\n",
    "    <img src=\"https://img.alicdn.com/imgextra/i1/O1CN01vbkBXC1HQ0SBrC1Ii_!!6000000000751-2-tps-1776-639.png\" width=\"600\"><br>\n",
    "\n",
    "\n",
    "<!-- \n",
    "它的整体流程图为：\n",
    "<img src=\"https://img.alicdn.com/imgextra/i1/O1CN01GjUPUs1dIbaxdDowf_!!6000000003713-2-tps-1971-1221.png\" width=\"600\" />\n",
    "1. **文档解析**<br>\n",
    "就像你将书上看到的信息理解为文字信息一样，RAG应用也需要首先将知识库文档进行加载并解析为大模型能够理解的文字形式；\n",
    "2. **文本分段**<br>\n",
    "你通常不会在做某道题时把整本书都翻阅一遍，而是查找与问题最相关的几个段落，分段方法可能是按照目录中的标题等方法，RAG应用也会在解析后将文本分段，以便于后续的检索。\n",
    "3. **索引建立**<br>\n",
    "在开卷考试时，通常会先找到与问题最相关的几个段落，然后根据这些段落进行回答。RAG应用也会使用嵌入模型（embedding 模型）对切片后的文本段建立索引，并以向量数据库形式存储，便于后续检索。\n",
    "使用嵌入模型（embedding 模型）对切片后的文档建立索引，并以向量数据库形式存储，便于后续检索；\n",
    "4. 发起提问：用户输入的问题与向量数据库的知识进行匹配，并通过大模型生成结合知识库与用户问题的回复。 -->"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 创建RAG应用\n",
    "\n",
    "构建一个RAG应用需要实现以上功能，这个过程并不容易。但通过LlamaIndex，你不需要过多代码就可以完成上述功能。\n",
    "\n",
    "与上一节教程一样，你需要运行以下代码将百炼API Key配置到环境中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-11-15T09:00:41.030766Z",
     "iopub.status.busy": "2024-11-15T09:00:41.030362Z",
     "iopub.status.idle": "2024-11-15T09:00:41.236899Z",
     "shell.execute_reply": "2024-11-15T09:00:41.236115Z",
     "shell.execute_reply.started": "2024-11-15T09:00:41.030739Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你配置的 API Key 是：sk-76*****\n"
     ]
    }
   ],
   "source": [
    "from config.load_key import load_key\n",
    "import os\n",
    "\n",
    "load_key()\n",
    "# 生产环境中请勿将 API Key 输出到日志中，避免泄露\n",
    "print(f'''你配置的 API Key 是：{os.environ[\"DASHSCOPE_API_KEY\"][:5]+\"*\"*5}''')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们在docs文件夹中准备了一些虚构的公司制度文件，接下来你将基于这些文件来创建RAG应用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-11-15T09:00:43.822829Z",
     "iopub.status.busy": "2024-11-15T09:00:43.822278Z",
     "iopub.status.idle": "2024-11-15T09:00:58.744414Z",
     "shell.execute_reply": "2024-11-15T09:00:58.743812Z",
     "shell.execute_reply.started": "2024-11-15T09:00:43.822800Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/haoznic/miniconda3/envs/learnacp/lib/python3.9/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "正在解析文件...\n",
      "正在创建索引...\n",
      "正在创建提问引擎...\n",
      "正在生成回复...\n",
      "回答是：\n",
      "对于公司项目管理，推荐使用Jira或Trello这样的项目管理软件。这些工具可以帮助团队更好地跟踪项目的进度，管理任务和时间，确保项目按时完成。同时，它们也支持团队成员之间的高效协作和沟通。"
     ]
    }
   ],
   "source": [
    "# 导入依赖\n",
    "from llama_index.embeddings.dashscope import DashScopeEmbedding,DashScopeTextEmbeddingModels\n",
    "from llama_index.core import SimpleDirectoryReader,VectorStoreIndex\n",
    "from llama_index.llms.openai_like import OpenAILike\n",
    "\n",
    "# 这两行代码是用于消除 WARNING 警告信息，避免干扰阅读学习，生产环境中建议根据需要来设置日志级别\n",
    "import logging\n",
    "logging.basicConfig(level=logging.ERROR)\n",
    "\n",
    "print(\"正在解析文件...\")\n",
    "# LlamaIndex提供了SimpleDirectoryReader方法，可以直接将指定文件夹中的文件加载为document对象，对应着解析过程\n",
    "documents = SimpleDirectoryReader('./docs').load_data()\n",
    "\n",
    "print(\"正在创建索引...\")\n",
    "# from_documents方法包含切片与建立索引步骤\n",
    "index = VectorStoreIndex.from_documents(\n",
    "    documents,\n",
    "    # 指定embedding 模型\n",
    "    embed_model=DashScopeEmbedding(\n",
    "        # 你也可以使用阿里云提供的其它embedding模型：https://help.aliyun.com/zh/model-studio/getting-started/models#3383780daf8hw\n",
    "        model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2\n",
    "    ))\n",
    "print(\"正在创建提问引擎...\")\n",
    "query_engine = index.as_query_engine(\n",
    "    # 设置为流式输出\n",
    "    streaming=True,\n",
    "    # 此处使用qwen-plus模型，你也可以使用阿里云提供的其它qwen的文本生成模型：https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5u\n",
    "    llm=OpenAILike(\n",
    "        model=\"qwen-plus\",\n",
    "        api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "        api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "        is_chat_model=True\n",
    "        ))\n",
    "print(\"正在生成回复...\")\n",
    "streaming_response = query_engine.query('我们公司项目管理应该用什么工具')\n",
    "print(\"回答是：\")\n",
    "# 采用流式输出\n",
    "streaming_response.print_response_stream()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 保存与加载索引\n",
    "你可能会发现，创建索引消耗的时间比较长。如果能够将索引保存到本地，并在需要使用的时候直接加载，而不是重新建立索引，那就可以大幅提升回复的速度，LlamaIndex提供了简单易实现的保存与加载索引的方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-11-15T09:00:59.966266Z",
     "iopub.status.busy": "2024-11-15T09:00:59.965889Z",
     "iopub.status.idle": "2024-11-15T09:01:00.240477Z",
     "shell.execute_reply": "2024-11-15T09:01:00.239682Z",
     "shell.execute_reply.started": "2024-11-15T09:00:59.966241Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "索引文件保存到了knowledge_base/test\n"
     ]
    }
   ],
   "source": [
    "# 将索引保存为本地文件\n",
    "index.storage_context.persist(\"knowledge_base/test\")\n",
    "print(\"索引文件保存到了knowledge_base/test\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-11-15T09:01:02.142167Z",
     "iopub.status.busy": "2024-11-15T09:01:02.141798Z",
     "iopub.status.idle": "2024-11-15T09:01:02.675970Z",
     "shell.execute_reply": "2024-11-15T09:01:02.675221Z",
     "shell.execute_reply.started": "2024-11-15T09:01:02.142142Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "成功从knowledge_base/test路径加载索引\n"
     ]
    }
   ],
   "source": [
    "# 将本地索引文件加载为索引\n",
    "from llama_index.core import StorageContext,load_index_from_storage\n",
    "storage_context = StorageContext.from_defaults(persist_dir=\"knowledge_base/test\")\n",
    "index = load_index_from_storage(storage_context,embed_model=DashScopeEmbedding(\n",
    "        model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2\n",
    "    ))\n",
    "print(\"成功从knowledge_base/test路径加载索引\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从本地加载索引后，你可以再次进行提问测试是否可以正常工作。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-11-15T09:01:11.982327Z",
     "iopub.status.busy": "2024-11-15T09:01:11.981943Z",
     "iopub.status.idle": "2024-11-15T09:01:14.921721Z",
     "shell.execute_reply": "2024-11-15T09:01:14.921129Z",
     "shell.execute_reply.started": "2024-11-15T09:01:11.982304Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "正在创建提问引擎...\n",
      "正在生成回复...\n",
      "回答是：\n",
      "对于公司项目管理，推荐使用Jira或Trello这样的项目管理软件。这些工具可以帮助团队更好地跟踪项目的进度，管理任务和时间，确保项目按计划顺利进行。同时，它们还支持团队成员之间的高效协作和沟通。"
     ]
    }
   ],
   "source": [
    "print(\"正在创建提问引擎...\")\n",
    "query_engine = index.as_query_engine(\n",
    "    # 设置为流式输出\n",
    "    streaming=True,\n",
    "    # 此处使用qwen-plus模型，你也可以使用阿里云提供的其它qwen的文本生成模型：https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5u\n",
    "    llm=OpenAILike(\n",
    "        model=\"qwen-plus\",\n",
    "        api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "        api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "        is_chat_model=True\n",
    "        ))\n",
    "print(\"正在生成回复...\")\n",
    "streaming_response = query_engine.query('我们公司项目管理应该用什么工具')\n",
    "print(\"回答是：\")\n",
    "streaming_response.print_response_stream()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你可以将上述代码进行封装，以便在后续持续迭代时快速使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-11-15T09:01:16.991663Z",
     "iopub.status.busy": "2024-11-15T09:01:16.991276Z",
     "iopub.status.idle": "2024-11-15T09:01:20.492123Z",
     "shell.execute_reply": "2024-11-15T09:01:20.491499Z",
     "shell.execute_reply.started": "2024-11-15T09:01:16.991640Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "公司项目管理可以使用项目管理软件如Jira或Trello。这些工具能够帮助团队更好地规划和跟踪项目的进度，确保任务按时完成。同时，它们也支持任务分配、时间线管理以及团队成员之间的协作，有助于提高工作效率和项目成功率。"
     ]
    }
   ],
   "source": [
    "from chatbot import rag\n",
    "\n",
    "# 引文在前面的步骤中已经建立了索引，因此这里可以直接加载索引。如果需要重建索引，可以增加一行代码：rag.indexing()\n",
    "index = rag.load_index(persist_path='./knowledge_base/test')\n",
    "query_engine = rag.create_query_engine(index=index)\n",
    "\n",
    "rag.ask('我们公司项目管理应该用什么工具', query_engine=query_engine)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 多轮对话\n",
    "RAG的多轮对话与直接向大模型发起多轮对话机制略有不同。你已经从2.1的教程中了解到多轮对话可以让大模型参考历史对话信息，实现方法为将历史对话信息添加到messages列表中。\n",
    "\n",
    "在RAG应用中的检索阶段，通常会去比较用户输入与文本段的语义相似度，但直接将用户输入与文本段进行相似度比较，可能会丢失历史对话信息，造成检索结果不准确。\n",
    "\n",
    "假设用户在第一轮对话提问：”张三工位在哪里？“后，在第二轮对话提问：”他的主管是谁？“，如果直接将第二轮对话中的提问与文本段进行相似度比较，检索系统并不知道“他”指代的是谁，因此大概率会检索出错误的文本段。\n",
    "\n",
    "如果将完整历史对话与问题都输入到检索系统，由于字数较多，检索系统可能无法处理（embedding模型在长文本上效果差于短文本）。业界常用的解决方法是：\n",
    "\n",
    "1. 通过大模型，基于历史对话信息，将用户的问题改写为一个新的query，新的query将包含历史对话的关键信息。\n",
    "2. 使用新的query，按照原先流程进行检索与生成的过程。\n",
    "\n",
    "LlamaIndex提供了便捷的工具，可以快速实现RAG应用的多轮对话。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-11-15T09:01:26.566550Z",
     "iopub.status.busy": "2024-11-15T09:01:26.566171Z",
     "iopub.status.idle": "2024-11-15T09:01:33.772277Z",
     "shell.execute_reply": "2024-11-15T09:01:33.771645Z",
     "shell.execute_reply.started": "2024-11-15T09:01:26.566525Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Querying with: 内容开发工程师的核心职责是什么？这职位属于综合技术岗位，但具体的工作内容和责任范围是怎样的？\n",
      "内容开发工程师的核心职责在于结合教育理论与技术实践，通过高质量的内容创造来支持学习者的成长与发展。这一职位属于技术大类下的综合技术岗位，具体的工作内容和责任范围包括：\n",
      "\n",
      "- **内容研究与分析**：深入研究最新的教育技术趋势、学习理论及市场需求，分析竞争对手产品，评估现有教育资源的有效性，并探索如何将新兴技术（例如人工智能、虚拟现实）融入教育内容中，确保内容在技术层面保持领先，同时满足教育者和学习者的需求。\n",
      "- **教材和课程开发**：依据研究结果和市场反馈，设计并开发高质量的教育教材和课程，涵盖教学大纲撰写、课件制作、评估工具设计等方面，确保内容符合教育标准和学习目标，提供全面的学习体验，同时兼顾不同学习者的需求，使内容适应多种学习风格和水平。\n",
      "- **内容优化与更新**：在内容开发过程中持续优化已有教育材料，通过收集学习者反馈和评价，发现并解决内容中的潜在问题，定期更新材料以反映最新的研究成果、技术进展和市场动态，保持内容的时效性和相关性。"
     ]
    }
   ],
   "source": [
    "from llama_index.core import PromptTemplate\n",
    "from llama_index.core.llms import ChatMessage, MessageRole\n",
    "from llama_index.core.chat_engine import CondenseQuestionChatEngine\n",
    "\n",
    "custom_prompt = PromptTemplate(\n",
    "    \"\"\"\\\n",
    "给定一段对话（在人类与助手之间）以及来自人类的后续信息，\n",
    "请将该信息改写为一个独立的问题，并在其中包含对话中的所有相关上下文。\n",
    "\n",
    "<聊天记录>\n",
    "{chat_history}\n",
    "\n",
    "<后续信息>\n",
    "{question}\n",
    "\n",
    "<独立的问题>\n",
    "\"\"\"\n",
    ")\n",
    "\n",
    "# 历史对话信息\n",
    "custom_chat_history = [\n",
    "    ChatMessage(role=MessageRole.USER,content=\"内容开发工程师细分类型是什么？\"),\n",
    "    ChatMessage(role=MessageRole.ASSISTANT, content=\"综合技术岗位。\"),\n",
    "]\n",
    "\n",
    "query_engine = index.as_query_engine(\n",
    "    # 设置为流式输出\n",
    "    streaming=True,\n",
    "    # 此处使用qwen-plus模型，你也可以使用阿里云提供的其它qwen的文本生成模型：https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5u\n",
    "    llm=OpenAILike(\n",
    "        model=\"qwen-plus\",\n",
    "        api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "        api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "        is_chat_model=True\n",
    "        ))\n",
    "chat_engine = CondenseQuestionChatEngine.from_defaults(\n",
    "    query_engine=query_engine,\n",
    "    condense_question_prompt=custom_prompt,\n",
    "    chat_history=custom_chat_history,\n",
    "    llm=OpenAILike(\n",
    "        model=\"qwen-plus\",\n",
    "        api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "        api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "        is_chat_model=True\n",
    "        ),\n",
    "    verbose=True\n",
    ")\n",
    "\n",
    "streaming_response = chat_engine.stream_chat(\"核心职责是什么\")\n",
    "for token in streaming_response.response_gen:\n",
    "    print(token, end=\"\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "虽然最后一个问题没有提到“内容开发工程师”，但大模型还是根据历史对话信息，将问题改写为“内容开发工程师的核心职责是什么？”，并给出了正确的答案。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 📝3.本节小结\n",
    "在本节课程中，你学习了以下内容：\n",
    "1. **RAG的工作原理**<br>\n",
    "一个完整的RAG应用通常包含建立索引与检索生成两个阶段，建立索引包括文档解析、文本分段、文本向量化、存储索引四个步骤，检索生成阶段包括检索与生成两个步骤。对RAG的工作原理有了了解后，你就可以更有针对性地对答疑机器人优化与迭代。\n",
    "2. **创建RAG应用**<br>\n",
    "通过LlamaIndex提供的高度集成化工具，你创建了一个RAG应用，并掌握了保存与加载索引的方法，同时你也学习了如何在RAG应用中实现多轮对话的方法。\n",
    "\n",
    "虽然答疑机器人已经可以很好地回答“我们公司项目管理应该用什么工具”等问题，但当前的功能还是比较简单，在后续的教程中我们会向你介绍拓展答疑机器人能力边界的方法。下一小节我们将介绍如何通过优化提示词来改善答疑机器人的回答质量。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 拓展阅读\n",
    "\n",
    "#### 文本向量化\n",
    "计算机并不能直接理解“我喜欢吃苹果”与“我爱吃苹果”这两句话到底有多相似，但它能理解两个相同维度的向量的相似度（通常使用余弦相似度来衡量）。文本向量化通过embedding模型，将自然语言转化为计算机能够理解的数字形式。\n",
    "\n",
    "embedding模型的训练通常会包含**对比学习**的环节，输入数据是许多已标记为是否相关的文本对(s1,s2)，模型的训练目标是尽可能让相关的文本对生成的向量相似度变高，不相关的文本对生成的向量相似度变低。\n",
    "\n",
    "在**建立索引**阶段，假设已经通过文本分段获得了n个chunk：[c1,c2,c3,...,cn]，那么embedding模型会将这n个chunk分别转化为向量：[v1,v2,v3,...,vn]，并存储为向量数据库。\n",
    "\n",
    "在**检索**阶段，假设用户的问题为q，那么embedding模型会将问题q转化为向量vq，并在向量数据库中找出与vq最相似的n个向量（这个值你可以自己设定），通过向量与文本段的索引关系得到文本段，作为检索结果。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 课后小测验\n",
    "【单选题】 在RAG应用中进行多轮对话，应该如何进行检索？\n",
    "\n",
    "A. 在检索阶段输入完整的历史对话信息<br>\n",
    "B. 结合历史对话信息对输入问题改写后进入检索阶段<br>\n",
    "C. 在检索阶段输入最新的输入问题<br>\n",
    "D. 将上一轮召回的文本段迁移过来<br>\n",
    "参考答案：B<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ✅评价反馈\n",
    "欢迎你参与[阿里云大模型ACP课程问卷](https://survey.aliyun.com/apps/zhiliao/Mo5O9vuie) 反馈学习体验和课程评价。\n",
    "你的批评和鼓励都是我们前进的动力！"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "learnacp",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
